"""
Base field definitions and abstract interfaces.
"""

using LinearAlgebra
using StaticArrays
using ..Grids

"""
    AbstractField

Abstract base type for all field implementations.

All concrete field types must implement:
- `grid(field)`: return the underlying grid
- `data(field)`: return the field data array
- `copy(field)`: create a deep copy
- Mathematical operations (+, -, *, /, etc.)
"""
abstract type AbstractField end

"""
    AbstractDataField <: AbstractField

Abstract base for fields that contain numerical data arrays.
"""
abstract type AbstractDataField <: AbstractField end

# Required interface methods

"""
    grid(field::AbstractField) -> AbstractGrid

Return the grid associated with the field.
"""
function grid end

"""
    data(field::AbstractField) -> AbstractArray

Return the numerical data array of the field.
"""
function data end

"""
    similar(field::AbstractField) -> AbstractField

Create a similar field with uninitialized data.
"""
function Base.similar end

"""
    copy(field::AbstractField) -> AbstractField

Create a deep copy of the field.
"""
function Base.copy end

"""
    zero(field::AbstractField) -> AbstractField

Create a field of the same type filled with zeros.
"""
function Base.zero(field::AbstractField)
    result = similar(field)
    fill!(data(result), 0)
    return result
end

"""
    one(field::AbstractField) -> AbstractField

Create a field of the same type filled with ones.
"""
function Base.one(field::AbstractField)
    result = similar(field)
    fill!(data(result), 1)
    return result
end

# Default implementations for common operations

"""
    size(field::AbstractField) -> Tuple{Vararg{Int}}

Return the size of the field data array.
"""
Base.size(field::AbstractField) = size(data(field))

"""
    length(field::AbstractField) -> Int

Return the total number of data points in the field.
"""
Base.length(field::AbstractField) = length(data(field))

"""
    eltype(field::AbstractField) -> Type

Return the element type of the field data.
"""
Base.eltype(field::AbstractField) = eltype(data(field))

"""
    ndims(field::AbstractField) -> Int

Return the number of dimensions of the field data array.
"""
Base.ndims(field::AbstractField) = ndims(data(field))

# Array-like interface
Base.getindex(field::AbstractField, i...) = getindex(data(field), i...)
Base.setindex!(field::AbstractField, v, i...) = setindex!(data(field), v, i...)
Base.firstindex(field::AbstractField, d=1) = firstindex(data(field), d)
Base.lastindex(field::AbstractField, d=1) = lastindex(data(field), d)

# Iterator interface
Base.iterate(field::AbstractField, state...) = iterate(data(field), state...)

# Comparison operations
function Base.:(==)(field1::AbstractField, field2::AbstractField)
    return (grid(field1) == grid(field2)) && (data(field1) == data(field2))
end

function Base.isapprox(field1::AbstractField, field2::AbstractField; kwargs...)
    return (grid(field1) == grid(field2)) && isapprox(data(field1), data(field2); kwargs...)
end

# Mathematical operations - these create new fields
function Base.:+(field1::AbstractField, field2::AbstractField)
    compatible_fields(field1, field2) || throw(ArgumentError("Fields must have compatible grids"))
    result = similar(field1)
    data(result) .= data(field1) .+ data(field2)
    return result
end

function Base.:-(field1::AbstractField, field2::AbstractField)
    compatible_fields(field1, field2) || throw(ArgumentError("Fields must have compatible grids"))
    result = similar(field1)
    data(result) .= data(field1) .- data(field2)
    return result
end

function Base.:*(field::AbstractField, scalar::Number)
    result = similar(field)
    data(result) .= data(field) .* scalar
    return result
end

function Base.:*(scalar::Number, field::AbstractField)
    return field * scalar
end

function Base.:/(field::AbstractField, scalar::Number)
    result = similar(field)
    data(result) .= data(field) ./ scalar
    return result
end

function Base.:-(field::AbstractField)
    result = similar(field)
    data(result) .= .-data(field)
    return result
end

# In-place mathematical operations
function Base.:+!(field1::AbstractField, field2::AbstractField)
    compatible_fields(field1, field2) || throw(ArgumentError("Fields must have compatible grids"))
    data(field1) .+= data(field2)
    return field1
end

function Base.:-!(field1::AbstractField, field2::AbstractField)
    compatible_fields(field1, field2) || throw(ArgumentError("Fields must have compatible grids"))
    data(field1) .-= data(field2)
    return field1
end

function Base.:*!(field::AbstractField, scalar::Number)
    data(field) .*= scalar
    return field
end

function Base.:/!(field::AbstractField, scalar::Number)
    data(field) ./= scalar
    return field
end

# Utility functions
"""
    compatible_fields(field1::AbstractField, field2::AbstractField) -> Bool

Check if two fields are compatible for mathematical operations.
"""
function compatible_fields(field1::AbstractField, field2::AbstractField)
    return grid(field1) == grid(field2)
end

"""
    fill!(field::AbstractField, value)

Fill the field with a constant value.
"""
function Base.fill!(field::AbstractField, value)
    fill!(data(field), value)
    return field
end

"""
    norm(field::AbstractField, p=2) -> Float64

Compute the p-norm of the field.
"""
function LinearAlgebra.norm(field::AbstractField, p=2)
    if p == 2
        # Use grid spacing for proper L2 norm
        g = grid(field)
        vol_element = prod(spacing(g))
        return sqrt(sum(abs2.(data(field))) * vol_element)
    else
        return norm(data(field), p)
    end
end

"""
    dot(field1::AbstractField, field2::AbstractField) -> Float64

Compute the inner product of two fields with proper grid weighting.
"""
function LinearAlgebra.dot(field1::AbstractField, field2::AbstractField)
    compatible_fields(field1, field2) || throw(ArgumentError("Fields must have compatible grids"))
    
    g = grid(field1)
    vol_element = prod(spacing(g))
    return sum(data(field1) .* data(field2)) * vol_element
end

# Display
function Base.show(io::IO, field::AbstractField)
    print(io, "$(typeof(field).name.name)(")
    print(io, "grid=$(grid(field)), ")
    print(io, "size=$(size(field))")
    print(io, ")")
end

function Base.show(io::IO, ::MIME"text/plain", field::AbstractField)
    println(io, typeof(field))
    println(io, "  Grid: $(grid(field))")
    println(io, "  Data size: $(size(field))")
    println(io, "  Data type: $(eltype(field))")
    
    # Show some statistics
    d = data(field)
    if length(d) > 0
        println(io, "  Range: [$(minimum(d)), $(maximum(d))]")
        println(io, "  Mean: $(mean(d))")
    end
end